using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using limpalex.bbparser.Interfaces;

namespace limpalex.bbparser
{
    public sealed partial class Replacement
    {
        private const string ParamRegexString = @"\${(?<name>\w+)\}";
        private static readonly Regex ParamRegex = new Regex(ParamRegexString, RegexOptions.Compiled);
        private readonly string[] _paramList;
        private readonly IAttributeEvaluator _evaluator;

        private readonly IList<IReplacementPart> _replacementParts = new List<IReplacementPart>();

        internal Replacement(string replacement, IAttributeEvaluator evaluator)
        {
            _evaluator = evaluator;

            var matches = ParamRegex.Matches(replacement);

            if (matches.Count == 0)
                _paramList = null;
            else
            {
                _paramList = new string[matches.Count];
                var index = 0;
                foreach (Match match in matches)
                {
                    _paramList[index] = match.Groups["name"].Value;
                    index++;
                }
            }

            if ( matches.Count == 0)
            {
                _replacementParts.Add(new ReplacementText(replacement));
            }
            else
            {
                var position = 0;
                foreach (Match match in matches)
                {
                    if ( match.Index > position )
                        _replacementParts.Add(new ReplacementText(replacement.Substring(position, match.Index - position)));
                    _replacementParts.Add(new ReplacementParameter(match.Groups["name"].Value, _evaluator));
                    position = match.Length + match.Index;
                }

                if ( position < replacement.Length )
                    _replacementParts.Add(new ReplacementText(replacement.Substring(position)));
            }
        }

        internal Replacement(string replacement):this(replacement,null) {}

        internal IEnumerable<string> GetParameters(bool includeCalculated)
        {
            foreach (var s in _paramList)
            {
                yield return s;
            }

            if (!includeCalculated || _evaluator == null)
            {
                yield break;
            }

            foreach (var attribute in _evaluator.SupportedAttributes)
            {
                yield return attribute;
            }
        }

        internal string Process(IParameterList parameters)
        {
            var builder = new StringBuilder();

            foreach (var part in _replacementParts)
            {
                part.Append(builder, parameters);
            }

            return builder.ToString();
        }
    }
}
